package io.gitee.zhangbinhub.acp.cloud.resource.server

import cn.dev33.satoken.SaManager
import cn.dev33.satoken.dao.SaTokenDao
import cn.dev33.satoken.dao.SaTokenDaoForRedisson
import cn.dev33.satoken.exception.NotLoginException
import cn.dev33.satoken.`fun`.SaFunction
import cn.dev33.satoken.oauth2.annotation.handler.SaCheckAccessTokenHandler
import cn.dev33.satoken.oauth2.annotation.handler.SaCheckClientIdSecretHandler
import cn.dev33.satoken.oauth2.annotation.handler.SaCheckClientTokenHandler
import cn.dev33.satoken.oauth2.template.SaOAuth2Util
import cn.dev33.satoken.router.SaHttpMethod
import cn.dev33.satoken.router.SaRouter
import cn.dev33.satoken.serializer.impl.SaSerializerTemplateForJdkUseBase64
import cn.dev33.satoken.solon.integration.SaTokenInterceptor
import cn.dev33.satoken.stp.StpUtil
import cn.dev33.satoken.strategy.SaAnnotationStrategy
import cn.dev33.satoken.util.SaTokenConsts
import io.gitee.zhangbinhub.acp.boot.exceptions.AcpWebExceptionHandler
import io.gitee.zhangbinhub.acp.cloud.constant.RestPrefix
import io.gitee.zhangbinhub.acp.cloud.resource.server.component.AcpCloudResourceServerGlobalFilter
import io.gitee.zhangbinhub.acp.cloud.resource.server.conf.AcpCloudResourceServerConfiguration
import io.gitee.zhangbinhub.acp.cloud.resource.server.constant.AcpCloudResourceServerConstant
import io.gitee.zhangbinhub.acp.cloud.resource.server.exceptions.AcpCloudResourceServerWebExceptionHandler
import io.gitee.zhangbinhub.acp.cloud.resource.server.tools.TokenTools
import io.gitee.zhangbinhub.acp.core.common.log.LogFactory
import org.noear.solon.Solon
import org.noear.solon.annotation.Bean
import org.noear.solon.annotation.Configuration
import org.noear.solon.annotation.Import
import org.noear.solon.annotation.Inject
import org.redisson.api.RedissonClient

@Configuration
@Import(AcpCloudResourceServerConfiguration::class)
class AcpCloudResourceServerAutoConfiguration {
    private val log = LogFactory.getInstance(this.javaClass)

    @Bean
    fun acpCloudResourceServerExceptionHandler(): AcpWebExceptionHandler = AcpCloudResourceServerWebExceptionHandler()

    @Bean(index = SaTokenConsts.SA_TOKEN_CONTEXT_FILTER_ORDER + 1)
    fun acpCloudResourceServerGlobalFilter(): AcpCloudResourceServerGlobalFilter = AcpCloudResourceServerGlobalFilter()

    @Bean
    fun saTokenDao(@Inject redissonClient: RedissonClient): SaTokenDao {
        return SaTokenDaoForRedisson(redissonClient)
    }

    @Bean
    fun saTokenSerializer() {
        SaManager.setSaSerializerTemplate(SaSerializerTemplateForJdkUseBase64())
    }

    @Bean(index = AcpCloudResourceServerConstant.RESOURCE_SERVER_SECURITY_FILTER_CHAIN_ORDER)
    fun saTokenInterceptor(@Inject acpCloudResourceServerConfiguration: AcpCloudResourceServerConfiguration): SaTokenInterceptor =
        SaTokenInterceptor().apply {
            val permitAll = permitAllPath(acpCloudResourceServerConfiguration)
            val security = acpCloudResourceServerConfiguration.securityPath.map { path ->
                Solon.cfg().serverContextPath() + path
            }.toMutableList().also { it.add("/**") }
            security.forEach { uri -> log.info("security uri: $uri") }
            permitAll.forEach { uri -> log.info("permitAll uri: $uri") }
            this.setIncludeList(security)
            this.setExcludeList(permitAll)
            this.setAuth {
                SaRouter.notMatch(SaHttpMethod.OPTIONS).match("/**", SaFunction {
                    val accessToken = TokenTools.getAccessTokenModel()
                    val clientToken = TokenTools.getClientTokenModel()
                    if (accessToken != null) {
                        SaOAuth2Util.checkAccessToken(accessToken.accessToken)
                        try {
                            StpUtil.checkLogin()
                        } catch (e: NotLoginException) {
                            throw NotLoginException("", "token 无效", "-2").setCode(11012)
                        }
                    }
                    if (clientToken != null) {
                        SaOAuth2Util.checkClientToken(clientToken.clientToken)
                    }
                    if (accessToken == null && clientToken == null) {
                        throw NotLoginException("", "token 无效", "-2").setCode(11012)
                    }
                })
            }
        }.also {
            SaAnnotationStrategy.instance.registerAnnotationHandler(SaCheckAccessTokenHandler())
            SaAnnotationStrategy.instance.registerAnnotationHandler(SaCheckClientIdSecretHandler())
            SaAnnotationStrategy.instance.registerAnnotationHandler(SaCheckClientTokenHandler())
        }

    companion object {
        /**
         * 放行安全检查的开放Path
         */
        fun permitAllPath(acpCloudResourceServerConfiguration: AcpCloudResourceServerConfiguration) =
            mutableListOf<String>().apply {
                this.add("${Solon.cfg().serverContextPath()}/error")
                this.add("${Solon.cfg().serverContextPath()}/favicon.ico")
                this.add("${Solon.cfg().serverContextPath()}/swagger/**")
                this.add("${Solon.cfg().serverContextPath()}/swagger-resources")
                this.add("${Solon.cfg().serverContextPath()}/v3/api-docs/**")
                this.add("${Solon.cfg().serverContextPath()}/doc.html")
                this.add("${Solon.cfg().serverContextPath()}/webjars/**")
                this.add("/solon-admin/api/monitor/data")
                this.add("/healthz")
                acpCloudResourceServerConfiguration.permitAllPath.forEach { path ->
                    this.add(
                        Solon.cfg().serverContextPath() + path
                    )
                }
                this.add(Solon.cfg().serverContextPath() + RestPrefix.OPEN + "/**")
            }
    }
}